<?php

namespace app\common\services;

use app\common\model\system\Menu;
use app\common\model\user\User;
use hiduoke\services\Random;
use think\Exception;
use think\facade\Db;
use think\facade\Request;
use think\facade\Validate;

class Auth
{
    protected static $instance = null;
    protected $_error = '';
    protected $_token = '';
    protected $_logined = false;
    //Token默认有效时长
    protected $keeptime = 2592000;
    protected $requestUri = '';
    protected $rules = [];
    //默认配置
    protected $config = [];
    protected $options = [];
    protected $hiddenFields = ['update_time', 'create_time', 'is_delete', 'join_ip', 'login_fail', 'login_ip', 'login_time', 'password', 'prev_time', 'salt', 'token'];

//    protected $hi
    /**
     * @var User|array|\think\Model
     */
    private $_user;


    public function __construct($options = [])
    {
    }

    /**
     * 根据Token初始化
     *
     * @param string $token Token
     * @return boolean
     */
    public function init($token)
    {
        if ($this->_logined) {
            return true;
        }
        if ($this->_error) {
            return false;
        }
        $uid = Token::get($token);
        if (!$uid) {
            return false;
        }
        $uid = intval($uid);
        if ($uid > 0) {
            $user = User::find($uid);
            if (!$user) {
                $this->setError('账号不存在');
                return false;
            }
            if ($user->status < 0) {
                $this->setError('账号已被封禁');
                return false;
            }
            $this->_user = $user;
            $this->_logined = true;
            $this->_token = $token;
            return true;
        } else {
            $this->setError('你还没有登录');
            return false;
        }
    }

    /**
     * 注册用户
     * @param string $username 用户名
     * @param string $password 密码
     * @param string $phone 手机号
     * @param array $extend 扩展参数
     * @return bool
     */
    public function register(string $username, string $password, $phone = '', $extend = [])
    {
        // 检测用户名、昵称、邮箱、手机号是否存在
        if ($username&&User::getByUsername($username)) {
            $this->setError('用户名已存在');
            return false;
        }

        if ($phone && User::getByPhone($phone)) {
            $this->setError('手机号已存在');
            return false;
        }

        $ip = request()->ip();
        $time = time();

        $data = [
            'username' => $username,
            'password' => $password,
            'phone'    => $phone,
            'avatar'   => '',
        ];
        $params = array_merge($data, [
            'salt'       => Random::alnum(),
            'join_ip'    => $ip,
            'login_time' => $time,
            'login_ip'   => $ip,
            'status'     => 0
        ]);
        $params['password'] = $this->getEncryptPassword($password, $params['salt']);
        $params = array_merge($params, $extend);
        // 启动事务
        Db::startTrans();
        try {
            $this->_user = User::create($params);
            $this->_token = Random::uuid();
            Token::set($this->_token, $this->_user->uid, $this->keeptime);
            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回滚事务
            $this->setError($e->getMessage());
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 用户登录
     *
     * @param string $account 账号,用户名、邮箱、手机号
     * @param string $password 密码
     * @return boolean
     */
    public function login($account, $password)
    {
        if (Validate::regex($account, '/^1\d{10}$/')) {
            $user = User::getByPhone($account);
        } else {
            $user = User::getByUsername($account);
        }

        if (!$user) {
            $this->setError('账号不存在');
            return false;
        }

        if ($user->status < 0) {
            $this->setError('账号已被封禁');
            return false;
        }

        if ($user->password != $this->getEncryptPassword($password, $user->salt)) {
            $this->setError('密码错误');
            $user->login_fail = $user->login_fail + 1;
            $user->save();
            return false;
        }
        //直接登录会员
        $this->direct($user->uid);
        return true;
    }

    /**
     * 直接登录账号
     * @param int $uid
     * @return boolean
     */
    public function direct($uid)
    {
        $user = User::find($uid);
        if ($user) {
            Db::startTrans();
            try {
                $ip = request()->ip();
                $time = time();

                $user->prev_time = $user->login_time;
                //记录本次登录的IP和时间
                $user->login_ip = $ip;
                $user->login_time = $time;
                //重置登录失败次数
                $user->login_fail = 0;

                $user->save();

                $this->_user = $user;

                $this->_token = Random::uuid();

                Token::set($this->_token, $user->uid, $this->keeptime);

                $this->_logined = true;
                // 登录成功事件
                event("user_login_success", $this->_user);
                Db::commit();
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * 退出
     *
     * @return boolean
     */
    public function logout()
    {
        if (!$this->_logined) {
            $this->setError('你还没有登录');
            return false;
        }
        //设置登录标识
        $this->_logined = false;
        //删除Token
        Token::delete($this->_token);
        // 触发退出登录成功事件
        event("user_logout_success");
        return true;
    }


    /**
     * 判断是否登录
     * @return boolean
     */
    public function isLogin()
    {
        if ($this->_logined) {
            return true;
        }
        return false;
    }


    /**
     * 获取当前Token
     * @return string
     */
    public function getToken()
    {
        return $this->_token;
    }

    /**
     * 修改密码
     * @param string $new_pwd 新密码
     * @param string $old_pwd 旧密码
     * @param bool $ignoreold_pwd 忽略旧密码
     * @return boolean
     */
    public function changePwd($new_pwd, $old_pwd = '', $ignore_old_pwd = false)
    {
        if (!$this->_logined) {
            $this->setError('你还没有登录');
            return false;
        }
        //判断旧密码是否正确
        if ($this->_user->password == $this->getEncryptPassword($old_pwd, $this->_user->salt) || $ignore_old_pwd) {
            Db::startTrans();
            try {
                $salt = Random::alnum();
                $new_pwd = $this->getEncryptPassword($new_pwd, $salt);
                $this->_user->save(['login_fail' => 0, 'password' => $new_pwd, 'salt' => $salt]);
                Token::delete($this->_token);
                //修改密码成功的事件
                Db::commit();
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return true;
        } else {
            $this->setError('密码错误');
            return false;
        }
    }

    /**
     * 修改用户密码
     * @param $uid
     * @param $pwd
     * @return bool
     */
    public function changePwdByUid($uid, $pwd)
    {
        $user = User::find($uid);
        if ($user) {
            Db::startTrans();
            try {
                $salt = Random::alnum();
                $pwd = $this->getEncryptPassword($pwd, $salt);
                $user->save(['login_fail' => 0, 'password' => $pwd, 'salt' => $salt]);
                //修改密码成功的事件
                event("user_change_password_success",$user);
                Db::commit();
                return true;
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
        } else {
            return false;
        }
    }


    /**
     * 获取会员基本信息
     */
    public function getUserinfo()
    {
        if (!$this->_logined) {
            $this->setError('用户未登录');
            return [];
        }
        $this->_user = $this->_user->hidden($this->hiddenFields)->append(['userGroup', 'roles', 'addon_roles', 'wallet']);
        $userinfo = $this->_user->toArray();
        return $userinfo;
    }

    /**
     * 删除一个指定会员
     * @param int $uid 会员ID
     * @return boolean
     */
    public function delete($uid)
    {
        $user = User::find($uid);
        if (!$user) {
            return false;
        }
        Db::startTrans();
        try {
            // 删除会员
            User::destroy($uid);
            // 删除会员指定的所有Token
            Token::clear($uid);
            Db::commit();
        } catch (Exception $e) {
            Db::rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 获取当前请求的URI
     * @return string
     */
    public function getRequestUri()
    {
        return $this->requestUri;
    }

    /**
     * 设置当前请求的URI
     * @param string $uri
     */
    public function setRequestUri($uri)
    {
        $this->requestUri = $uri;
    }

    /**
     * 获取密码加密后的字符串
     * @param string $password 密码
     * @param string $salt 密码盐
     * @return string
     */
    public function getEncryptPassword($password, $salt = '')
    {
        return md5(md5($password) . $salt);
    }

    /**
     * 设置会话有效时间
     * @param int $keeptime 默认为永久
     */
    public function keeptime($keeptime = 0)
    {
        $this->keeptime = $keeptime;
    }

    /**
     * 匹配控制器的方法
     *
     * @param array|string $arr 需要验证权限的数组
     * @return boolean
     */
    public function match($arr = [])
    {
        $request = request();
        $arr = is_array($arr) ? $arr : explode(',', $arr);
        if (!$arr) {
            return false;
        }
        $arr = array_map('strtolower', $arr);
        if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) {
            return true;
        }
        return false;
    }


    /**
     * 设置错误信息
     *
     * @param string $error 错误信息
     * @return Auth
     */
    public function setError($error)
    {
        $this->_error = $error;
        return $this;
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError()
    {
        return $this->_error;
    }


    /**
     * 获取会员菜单列表
     * @return array
     */
    public function getMenuList()
    {
        if ($this->rules) {
            return $this->rules;
        }
        $group = $this->_user->userGroup ?? false;
        if (!$group) {
            return [];
        }
        if ($this->isFounder()) {
            $this->rules = (new Menu())->field('id,pid,api_url,methods,menu_name,menu_type,auth_mark')->select();
        } else {
            $this->rules = Menu::whereIn('id', $group->rules)->field('id,pid,api_url,methods,menu_name,menu_type,auth_mark')->select();
        }
        return $this->rules->toArray();
    }

    /**
     * 检测接口是否是否有对应权限
     * @param null $api_url 请求地址
     * @param array $methods 请求方法
     * @return bool
     */
    public function check($api_url = null, $methods = [])
    {
        if ($this->isFounder()) return true;
        $request =  request();
        $api_url = $api_url ?: $request->pathinfo();
        $methods = $methods ?: [$request->method()];
        $api_url = str_replace('/adminapi', '', $api_url);
        $has_menu = Menu::where('api_url', $api_url)->find();
        if ($has_menu) {
            $ruleList = $this->getMenuList();
            foreach ($ruleList as $k => $v) {
                if ($v['api_url'] == $api_url && in_array($v['methods'], $methods)) {
                    return true;
                }
            }
            return false;
        } else {
            return true;
        }
    }

    public function isFounder($uid = 0)
    {
        $founder = env('app.founder');
        if (!$uid && $this->getUserinfo()) {
            $uid = $this->getUserinfo()['uid'];
        }
        if ($founder) {
            $founders = explode(',', $founder);
            return in_array($uid, $founders);
        } else {
            return false;
        }
    }

}
